;	XEROX REQUIRES 4MHZ ZPU OPERATION
	EXTRN	BINH2
XEROX:	
	ld	hl,(6)
	ld	sp,hl
	LD	A,(5CH);	CDOS FCB-1
	LD	(TO),A
	LD	A,(6CH);	CDOS FCB-2
	LD	(FROM),A
	LD	A,(TO)
	OR	A
	JR	NZ,NXT
	LD	C,C.DISK
	CALL	CDOS
	INC	A
	LD	(TO),A
NXT:
	LD	A,(FROM)
	OR	A
	JR	NZ,NXT2
	LD	C,C.DISK
	CALL	CDOS
	INC	A
	LD	(FROM),A
NXT2:
	LD	HL,TO
	CP	(HL)
	JR	NZ,NXT2A
	LD	DE,NOTSAME
	LD	C,W.LINE
	CALL	CDOS
	JP	0
NOTSAME:DB	LF,CR,BEL,BEL,BEL,'CANNOT COPY TO SAME FLOPPY',LF,CR,'$'
NXT2A:	
	LD	A,(TO)
	ADD	40H
	LD	(DR),A
	LD	DE,QUEST
	LD	C,W.LINE
	CALL	CDOS
	LD	C,R.CHAR
	CALL	CDOS
	CP	'Y'
	JR	Z,OK
	CP	'y'
	JR	Z,OK
	JP	0
QUEST:	DB	LF,CR,BEL,BEL,BEL,'OK TO DESTROY CURRENT CONTENTS OF FLOPPY ON DRIVE '
DR:	DB	' ? $'
CRLF:	DB	LF,CR,'$'
FROM:	DB	0
TO:	DB	0
CURR:	DB	0
SAMEDR:	DB	0
DENSITY: DB	0
RETRY:	DB	0
HLSAVE:	DW	0
TRKSINMEM: DB	0
TRKIN:	DB	0
TRKOUT:	DB	0
TRKCNT:	DB	0
	ENTRY	$MEMRY
$MEMRY:	DW	0
OK:
	LD	DE,CRLF
	LD	C,W.LINE
	CALL	CDOS
	LD	A,0;	TEST IF FLOPPIES ON SAME DUAL DRIVE
	LD	(SAMEDR),A
	LD	A,(FROM)
	LD	B,A
	LD	A,(TO)
	SUB	B
	JP	P,POSA
	NEG
POSA:	CP	1
	JR	NZ,NOTONE;	NO
	LD	A,(TO);		IF DIFFERENCE IS ONE IT MUST BE A 1,2 OR 3,4
	CP	B;						COMBINATION
	JP	P,AGR
	LD	A,B
AGR:	RRA;			I.E. THE LARGEST MUST BE EVEN
	JR	C,NOTONE;		NOT ON SAME DUAL DRIVE
	LD	(SAMEDR),A
NOTONE:	
	LD	A,(FROM)
	LD	B,A
	XOR	A
	SCF
NXT3:	RLA
	DJNZ	NXT3
	OR	MOTORON+MAXI+DBLDEN
	LD	(FROM),A
	LD	A,(TO)
	LD	B,A
	XOR	A
	SCF
NXT4:	RLA
	DJNZ	NXT4
	OR	MOTORON+MAXI+DBLDEN
	LD	(TO),A
MSKINTRP: EQU	03H
AUXDSK: EQU	04H;		INPUT/OUTPUT PORT FOR FAST SEEK
	FASTSEEK: EQU	0EFH
	SEEKCOMP: EQU	40H
	RESETFST: EQU	0FFH
;
DSKSTAT:EQU	30H;		INPUT PORT FOR DISK STATUS
	NOTREADY: EQU	80H
	PROTECT:EQU	40H
	HEADDOWN: EQU	20H
	WRFAULT:EQU	20H
	NOTFND: EQU	10H
	CRCERR: EQU	08H
	LOSTDATA: EQU	04H
	DRQ:	EQU	02H
	BUSY:	EQU	01H
;
DSKCMD:	EQU	30H;		OUTPUT PORT FOR DISK COMMANDS
	RESTORE:EQU	0CH
	SEEK:	EQU	18H
	READ:	EQU	88H
	ENBLHOLD: EQU	04H
	WRITE:	EQU	0A8H
	RESET:	EQU	0D0H
;
TRKREG:	EQU	31H;		I/O PORT FOR TRACK REGISTER
;
SECREG:	EQU	32H;		I/O PORT FOR SECTOR REGISTER
;
DATAREG:EQU	33H;		I/O PORT FOR DATA
;
DSKCNTRL:EQU	34H;		OUTPUT PORT FOR DISK CONTROL
	MOTORON:EQU	20H
	MAXI:	EQU	10H
	DBLDEN:	EQU	40H
	AUTOWAIT: EQU	80H
;
DSKFLGS:EQU	34H;		INPUT PORT FOR DISK FLAGS
	HEADLOAD: EQU	20H
	EOJ:	EQU	01H
;
CDOS:	EQU	5;		CDOS CALL ADDRESS
;	CDOS CALL CODES
	C.DISK: EQU	25
	R.CHAR: EQU	1
	W.LINE: EQU	9
	DIVIDE: EQU	138
	RES.CDOS: EQU	13
	SEL.DISK: EQU	14
;	TTY CONTROL CODES
CR:	EQU	0DH
LF:	EQU	0AH
BEL:	EQU	07H
;	DISK CONSTANTS
MAXRETRY: EQU	10	; NUMBER OF RETRIES IF R/W ERROR
NRTRKS:	EQU	77
NRSECS:	EQU	26	; SINGLE DEN
NRSECS2: EQU	16	; DOUBLE DEN
TRKSIZE:EQU	NRSECS2*512;	SIZE OF A TRACK
TRKSIZ1:EQU	NRSECS*128
	XOR	A
	LD	(TRKIN),A
	LD	(TRKOUT),A
	LD	HL,($MEMRY);	GET START OF BUFFER
	EX	DE,HL;		SAVE IN DE
	LD	HL,(6);		GET HIGHEST MEMORY ADDRESS
	SCF	
	SBC	HL,DE;		CALCULATE AVAILABLE MEMORY
	LD	DE,TRKSIZE;	SIZE OF A TRACK
	LD	C,DIVIDE
	CALL	CDOS
	LD	A,L
	LD	(TRKSINMEM),A;	SET MAX. NR OF TRACKS READABLE
	LD	A,(FROM)
	AND	NOT DBLDEN	; DISABLE DOUBLE DENSITY FOR HOME
	LD	(CURR),A
	OUT	DSKCNTRL,A;	SELECT INPUT DISK
	LD	D,NOTREADY+NOTFND+CRCERR+BUSY
	LD	A,RESTORE;	RESTORE DISK TO TRACK ZERO
	CALL	EXECUTE
	LD	A,(TO)
	AND	NOT DBLDEN
	LD	(CURR),A
	OUT	DSKCNTRL,A
	LD	D,NOTREADY+PROTECT+NOTFND+CRCERR+BUSY
	LD	A,RESTORE
	CALL	EXECUTE
; READ IN FIRST SECTOR TO SEE IF DOUBLE OR SINGLE DENSITY
	LD	HL,($MEMRY)
	LD	A,(FROM)
	AND	NOT DBLDEN
	LD	(CURR),A
	CALL	SETUP
	ADD	READ
	LD	D,NOTREADY+NOTFND+CRCERR+LOSTDATA
	LD	C,SECREG
	LD	E,1
	OUT	(C),E
	LD	C,DATAREG
	OUT	DSKCMD,A;	START READING
LOOP2:	
	IN	A,DSKFLGS;	WAIT FOR DRQ OR EOJ
	RRA
	JR	C,DENSREADY;	EOJ FOUND?
	INI;			PLACE NEXT BYTE AT (HL) AND UPDATE
	JR	LOOP2;		GET NEXT BYTE FROM SECTOR
DENSREADY:
	IN	A,DSKSTAT;	GET STATUS AFTER SECTOR
	LD	B,A
	AND	D
	JP	NZ,ERROR
	LD	IX,($MEMRY)
	LD	A,'L'
	CP	(IX+78H)
	JR	NZ,SINGLE
	LD	A,'G'
	CP	(IX+79H)
	JR	NZ,SINGLE
	LD	A,'S'
	CP	(IX+7AH)
	JR	NZ,SINGLE
	CP	(IX+7BH)
	JR	NZ,SINGLE
	LD	A,'D'
	CP	(IX+7CH)
	JR	NZ,SINGLE
	CP	(IX+7DH)
	JR	NZ,SINGLE
	LD	A,0FFH
	LD	(DENSITY),A
	LD	DE,DOUBLEDEN
	JR	DENMESS
SINGLE:
	XOR	A
	LD	(DENSITY),A
	LD	DE,SINGLEDEN
DENMESS:
	LD	C,W.LINE
	CALL	CDOS
	JR	RESTART
DOUBLEDEN: DB	'Double density transfer.',CR,LF,'$'
SINGLEDEN: DB	'Single density transfer.',CR,LF,'$'
RESTART:
	LD	HL,($MEMRY)
	LD	A,0
	LD	(TRKCNT),A
	LD	A,(DENSITY)
	OR	A
	JR	Z,SNGSKP1
	IN	A,TRKREG
	OR	A	; CHECK FOR TRACK 2 OR GREATER
SNGSKP1: LD	A,(FROM)
	JR	NZ,DBLSKP1
	AND	NOT DBLDEN
DBLSKP1:
	LD	(CURR),A
	CALL	SETUP;		PREPARE DISK FOR COMING OPERATION
	ADD	READ;		CONSTRUCT READ COMMAND
RDTRACK:
	LD	E,1;		SECTOR	COUNTER
	LD	D,NOTREADY+NOTFND+CRCERR+LOSTDATA
	PUSH	HL
	LD	HL,RETRY
	LD	(HL),0
	POP	HL
AGAIN:
	LD	(HLSAVE),HL	;SAVE FOR RETRIES
	LD	C,SECREG
	OUT	(C),E
	LD	C,DATAREG
	OUT	DSKCMD,A;	START READING
AGAIN2:	
	IN	A,DSKFLGS;	WAIT FOR DRQ OR EOJ
	RRA
	JR	C,READREADY;	EOJ FOUND?
	INI;			PLACE NEXT BYTE AT (HL) AND UPDATE
	JR	AGAIN2;		GET NEXT BYTE FROM SECTOR
READREADY:
	IN	A,DSKSTAT;	GET STATUS AFTER SECTOR
	LD	B,A
	AND	D
	JP	NZ,RERROR
	LD	A,E
	CP	NRSECS
	JP	Z,LSTSEC;	LAST SECTOR OF TRACK INPUT
	LD	A,(DENSITY)
	OR	A
	JR	Z,DBLSKP4
	IN	A,TRKREG	; CHECK FOR DBLDEN
	OR	A
	JR	Z,DBLSKP4
	LD	A,E
	CP	NRSECS2
	JP	Z,LSTSEC
DBLSKP4: INC	E
RRETRY:
	LD	A,(DENSITY)
	OR	A
	JR	Z,SNGSKP8
	IN	A,TRKREG
	OR	A	; CHECK FOR TRACK 2 OR GREATER
SNGSKP8:
	LD	A,(FROM)
	JR	NZ,DBLSKP8
	AND	NOT DBLDEN
DBLSKP8:
	OR	AUTOWAIT
	OUT	DSKCNTRL,A
	LD	A,READ
	JR	AGAIN
RERROR:
	LD	A,(RETRY)
	CP	MAXRETRY
	JP	Z,ERROR
	INC	A
	LD	(RETRY),A
	PUSH	DE
	PUSH	BC
	LD	A,B
	LD	HL,RSTAT;	SET ERROR OR STATUS BITS
	CALL	BINH2
	IN	A,TRKREG;	GET TRACK-NUMBER
	LD	HL,RTRK
	CALL	BINH2
	IN	A,SECREG;	GET SECTOR-NUMBER
	LD	HL,RSEC
	CALL	BINH2
	LD	DE,MESRETRY
	LD	C,W.LINE
	CALL	CDOS
	POP	BC
	POP	DE
	LD	HL,(HLSAVE)
	JR	RRETRY
MESRETRY: DB	'Retry, Status = '
RSTAT:	DB	'  , Track = '
RTRK:	DB	'  , Sector = '
RSEC:	DB	'  .',CR,LF,'$'
LSTSEC:
	LD	A,(TRKIN);	DO IT FOR 77 TRACKS
	CP	NRTRKS-1
	JP	Z,WRTHEM
	INC	A
	LD	(TRKIN),A
	LD	B,A
	DEC	A
	CALL	SEEKTRK;	GO TO NEXT TRACK
	LD	A,(TRKSINMEM)
	LD	B,A
	LD	A,(TRKCNT)
	INC	A
	CP	B
	JP	Z,WRTHEM
	LD	(TRKCNT),A
	LD	A,(DENSITY)
	OR	A
	JR	Z,SNGSKPA
	IN	A,TRKREG
	OR	A	; CHECK FOR TRACK 2 OR GREATER
SNGSKPA:
	LD	A,(FROM)
	JR	NZ,DBLSKPA
	AND	NOT DBLDEN
DBLSKPA:
	OR	AUTOWAIT
	OUT	DSKCNTRL,A
	LD	A,READ
	JP	RDTRACK
WRTHEM:
	LD	HL,($MEMRY)
	LD	A,0
	LD	(TRKCNT),A
	LD	A,(SAMEDR);	TEST IF ON SAME DUAL DRIVE IS INPUT
	OR	A
	JR	Z,DIFDR;	NO
	LD	A,(TRKOUT)
	LD	B,A
	LD	A,(TRKIN)
	CALL	SEEKTRK;	SEEK BACK TO WRITING POSITION
DIFDR:
	LD	A,(TRKOUT)
	OUT	TRKREG,A
	LD	A,(DENSITY)
	OR	A
	JR	Z,SNGSKP2
	IN	A,TRKREG
	OR	A	; CHECK FOR TRACK 2 OR GREATER
SNGSKP2:
	LD	A,(TO)
	JR	NZ,DBLSKP2
	AND	NOT DBLDEN
DBLSKP2:
	LD	(CURR),A
	CALL	SETUP;		PREPARE DISK FOR COMING OPERATION
	ADD	WRITE;		CONTRUCT WRITE COMMAND
WRTRACK:
	LD	E,1;		SECTOR COUNTER
	LD	D,NOTREADY+PROTECT+WRFAULT+NOTFND+CRCERR+LOSTDATA
BACK:
	LD	C,SECREG
	OUT	(C),E
	LD	C,DATAREG
	OUT	DSKCMD,A;	START WRITING
BACK2:
	IN	A,DSKFLGS;	WAIT FOR DRQ OR EOJ
	RRA
	JR	C,WRREADY;	EOJ FOUND?
	OUTI;			GET NEXT BYTE FROM (HL) AND UPDATE
	JR	BACK2;		WRITE NEXT BYTE TO SECTOR
WRREADY:
	IN	A,DSKSTAT;	GET STATUS AFTER SECTOR
	LD	B,A
	AND	D
	JP	NZ,ERROR
	LD	A,E;
	CP	NRSECS
	JR	Z,VERIFY;	LAST SECTOR OF TRACK WRITTEN
	LD	A,(DENSITY)
	OR	A
	JR	Z,DBLSKP5
	IN	A,TRKREG	; CHECK FOR DBLDEN
	OR	A
	JR	Z,DBLSKP5
	LD	A,E
	CP	NRSECS2
	JR	Z,VERIFY
DBLSKP5: INC	E
	LD	A,(DENSITY)
	OR	A
	JR	Z,SNGSKP9
	IN	A,TRKREG
	OR	A	; CHECK FOR TRACK 2 OR GREATER
SNGSKP9:
	LD	A,(TO)
	JR	NZ,DBLSKP9
	AND	NOT DBLDEN
DBLSKP9:
	OR	AUTOWAIT
	OUT	DSKCNTRL,A
	LD	A,WRITE
	JR	BACK
VERIFY:
	LD	E,1;		RESTART AT FIRST SECTOR
	LD	D,NOTREADY+NOTFND+CRCERR+LOSTDATA
BACK3:
	LD	A,(DENSITY)
	OR	A
	JR	Z,SNGSKP3
	LD	A,(TRKOUT)
	OR	A	; CHECK FOR TRACK 2 OR GREATER
SNGSKP3:
	LD	A,(TO)
	JR	NZ,DBLSKP3
	AND	NOT DBLDEN
DBLSKP3:
	OR	AUTOWAIT
	OUT	DSKCNTRL,A
	LD	B,0FFH;		SET ERROR CODE TO "VERIFY ERROR"
	LD	C,SECREG
	OUT	(C),E
	LD	A,READ
	OUT	DSKCMD,A;	START READING
BACK4:
	IN	A,DSKFLGS;	WAIT FOR DRQ OR EOJ
	RRA
	JR	C,SECREAD;	IF EOJ, SECTOR IS READ
	IN	A,DATAREG;	GET BYTE
	JR	BACK4
SECREAD:
	IN	A,DSKSTAT;	STATUS AFTER SECTOR READ
	LD	B,A
	AND	D
	JP	NZ,ERROR
	LD	A,E;		LAST SECTOR OF TRACK?
	CP	NRSECS
	JR	Z,ENDVERIFY
	LD	A,(DENSITY)
	OR	A
	JR	Z,DBLSKP6
	IN	A,TRKREG	; CHECK FOR DBLDEN
	OR	A
	JR	Z,DBLSKP6
	LD	A,E
	CP	NRSECS2
	JR	Z,ENDVERIFY
DBLSKP6: INC	E
	JR	BACK3
ENDVERIFY:
	LD	A,(TRKOUT);	DO IT FOR 77 TRACKS
	CP	NRTRKS-1
	JP	Z,TERM
	INC	A
	LD	(TRKOUT),A
	LD	B,A
	DEC	A
	CALL	SEEKTRK;	GO TO NEXT TRACK
	LD	A,(TRKSINMEM);	CHECK IF END OF MEMORY
	LD	B,A
	LD	A,(TRKCNT)
	INC	A
	CP	B
	JP	Z,RESTART
	LD	(TRKCNT),A
	LD	A,(DENSITY)
	OR	A
	JR	Z,SNGSKPB
	IN	A,TRKREG
	OR	A
SNGSKPB:
	LD	A,(TO)
	JR	NZ,DBLSKPB
	AND	NOT DBLDEN
DBLSKPB:
	OR	AUTOWAIT
	OUT	DSKCNTRL,A
	LD	A,WRITE
	JP	WRTRACK
;
;THIS SUBROUTINE IS USED AFTER SWITCH TO NEW DRIVE TO PREPARE IT
;FOR A NEW SEQUENCE OF READS OR WRITES
;
SETUP:
	OUT	DSKCNTRL,A
	LD	B,A;		READ/WRITE FROM FIRST SECTOR ONWARDS
	LD	A,1;
	OUT	SECREG,A
	IN	A,DSKFLGS
	AND	HEADLOAD;	HEAD LOADED?
	LD	C,A
	LD	A,B
	OR	A,AUTOWAIT;	FOR NEXT COMMANDS USE AUTO WAIT MODE
	OUT	DSKCNTRL,A
	LD	A,C
	OR	A;		WAS HEAD LOADED?
	LD	A,4
	RET	Z;		NO
	XOR	A
	RET
;
;THIS SUBROUTINE EXECUTES "RESTORES" AND "SEEKS" (ERROR MASK IN REG.D)
;
EXECUTE:
	OUT	DSKCMD,A
WAIT:
	IN	A,DSKFLGS;	CHECK IF READY
	RRA
	JR	NC,WAIT
	IN	A,DSKSTAT
	LD	B,A
	AND	D;		COMPARE TO ERROR MASK
	JR	NZ,ERROR
	RET
;
;THIS ROUTINE EXECUTES A FAST SEEK FROM (A) TO (B)
;
SEEKTRK:
	OUT	TRKREG,A;	SET TO CURRENT TRACK
	LD	A,B;		GET REQUIRED TRACK-NR
	OUT	DATAREG,A;	GIVE DESIRED TRACK TO 4FDC
	LD	A,FASTSEEK;	SET TO FAST SEEK
	OUT	AUXDSK,A
	LD	A,SEEK;
	LD	D,NOTREADY
	CALL	EXECUTE
REDO2:	IN	A,AUXDSK;	SEEK COMPLETED?
	AND	SEEKCOMP
	JR	NZ,REDO2
	LD	A,RESETFST;	RESET FAST SEEK MODE
	OUT	AUXDSK,A
	RET
;
;THIS HANDLES ANY ERROR AND TERMINATES THE PROGRAM WITH AN APPROPRIATE
;ERROR MESSAGE
;
ERROR:
	PUSH	BC;	SAVE ERROR STATUS
	LD	A,B
	LD	HL,ERSTAT;	SET ERROR OR STATUS BITS
	CALL	BINH2
	IN	A,TRKREG;	GET TRACK-NUMBER
	LD	HL,TRK
	CALL	BINH2
	IN	A,SECREG;	GET SECTOR-NUMBER
	LD	HL,SEC
	CALL	BINH2
	LD	A,RESET;	RESET ANY CURRENT COMMAND
	OUT	DSKCMD,A
	LD	A,(CURR)
	AND	0FH
	LD	B,1
RRA:	RRA	;		FIND OUT DEVICE NAME
	JR	C,DEVFND
	INC	B
	JR	RRA
DEVFND: LD	A,B
	ADD	40H
	LD	(DEV),A
	LD	DE,ERMSG
	LD	C,W.LINE
	CALL	CDOS
	POP	BC;		RESTORE ERROR FLAG
	LD	A,B
	LD	DE,VERFERR
	CP	0FFH
	JR	Z,ERTERM;	ERROR FOUND IN VERIFICATION
	LD	DE,IOERR
	AND	NOTFND+CRCERR
	JR	NZ,ERTERM;	REAL READ OR WRITE ERROR
	LD	A,B
	LD	DE,READYERR
	AND	NOTREADY
	JR	NZ,ERTERM;	DRIVE IS NOT READY
	LD	A,B
	LD	DE,PROTERR
	AND 	PROTECT
	JR	NZ,ERTERM;	FLOPPY IS WRITE PROTECTED
	LD	A,B
	LD	DE,WRERR
	AND	WRFAULT
	JR	NZ,ERTERM;	I/O ERROR ON WRITING
	LD	DE,UNKNERR;	STRANGE ERROR
ERTERM:
	LD	C,W.LINE;	PREPARE FOR WRITE MESSAGE
	CALL	CDOS
	LD	DE,ERMSG2
	CALL	CDOS
	JP	TERM2
VERFERR:DB	'(DATA READ BACK UNEQUAL TO DATA WRITTEN)$'
IOERR:	DB	'(DATA ERROR WHEN READING OR WRITING)$'
READYERR: DB	'(DRIVE NOT READY: MAKE READY AND RETRY)$'
PROTERR:DB	'(FLOPPY IS WRITE PROTECTED)$'
WRERR:	DB	'(DATA ERROR WHEN WRITING: RE-INITIALIZE FLOPPY)$'
UNKNERR:DB	'(UNIDENTIFIABLE PROBLEM)$'
ERMSG:	DB	BEL,BEL,BEL,'ERROR ',BEL,BEL,'ON DRIVE ',BEL,BEL,BEL
DEV:	DB	' , AT TRACK '
TRK:	DB	'  , SECTOR '
SEC:	DB	'  ,',LF,CR,'STATUS='
ERSTAT: DB	'   $'
ERMSG2: DB	'.',LF,CR,'PROGRAM PREMATURELY TERMINATED.$'
TRMMSG:	DB	'FAST COPY OPERATION SUCCEEDED. FLOPPY WRITTEN AND VERIFIED.$'
;
;THIS TERMINATES THE PROGRAM AFTER RESTORING THE "CURRENT DISK" SELECTION
;
TERM:
	LD	C,W.LINE
	LD	DE,TRMMSG
	CALL	CDOS
TERM2:
	LD	C,C.DISK;	GET CURRENT DRIVE
	CALL	CDOS
	LD	E,A
	LD	C,RES.CDOS;	RESET CDOS AND LOG OFF ALL DISKS
	CALL	CDOS
	LD	C,SEL.DISK;	RESTORE CURRENT DISK SELECTION
	CALL	CDOS
	JP	100H
	END	XEROX
